package com.doubao.service;

import com.doubao.config.MinioConfig;
import com.doubao.exception.BusinessException;
import io.minio.*;
import io.minio.errors.ErrorResponseException;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import static javax.swing.UIManager.put;

@Slf4j
@Service
@RequiredArgsConstructor
public class MinioService {
    private final MinioClient minioClient;
    private final MinioConfig minioConfig;

    // 添加常量定义
    private static final long MAX_FILE_SIZE = 10 * 1024 * 1024; // 10MB
    private static final String[] ALLOWED_CONTENT_TYPES = {
            "image/jpeg", "image/png", "image/gif", "image/webp"
    };

//    private static final Map<String, String> EXTENSION_TO_CONTENT_TYPE = Map.of(
//            ".jpg", "image/jpeg",
//            ".jpeg", "image/jpeg",
//            ".png", "image/png",
//            ".gif", "image/gif",
//            ".webp", "image/webp"
//    );
    //jdk1.8
    private static final Map<String, String> EXTENSION_TO_CONTENT_TYPE = Collections.unmodifiableMap(new HashMap<String, String>() {{
        put(".jpg", "image/jpeg");
        put(".jpeg", "image/jpeg");
        put(".png", "image/png");
        put(".gif", "image/gif");
        put(".webp", "image/webp");
    }});

    @PostConstruct
    public void init() {
        try {
            // 检查存储桶是否存在
            boolean exists = minioClient.bucketExists(
                    BucketExistsArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .build()
            );

            if (!exists) {
                // 创建存储桶
                minioClient.makeBucket(
                        MakeBucketArgs.builder()
                                .bucket(minioConfig.getBucketName())
                                .build()
                );

                // 设置存储桶策略为公共读
                String publicPolicy = String.format(
                        "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::%s/*\"]}]}",
                        minioConfig.getBucketName()
                );

                minioClient.setBucketPolicy(
                        SetBucketPolicyArgs.builder()
                                .bucket(minioConfig.getBucketName())
                                .config(publicPolicy)
                                .build()
                );
            }

            // 确保已存在的存储桶也有正确的访问策略
            String currentPolicy = minioClient.getBucketPolicy(
                    GetBucketPolicyArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .build()
            );

            if (!currentPolicy.contains("s3:GetObject")) {
                // 如果当前策略不包含公共读权限，则更新策略
                String publicPolicy = String.format(
                        "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Principal\":{\"AWS\":[\"*\"]},\"Action\":[\"s3:GetObject\"],\"Resource\":[\"arn:aws:s3:::%s/*\"]}]}",
                        minioConfig.getBucketName()
                );

                minioClient.setBucketPolicy(
                        SetBucketPolicyArgs.builder()
                                .bucket(minioConfig.getBucketName())
                                .config(publicPolicy)
                                .build()
                );
            }

        } catch (Exception e) {
            log.error("MinIO初始化失败: {}", e.getMessage(), e);
            throw new BusinessException("MinIO初始化失败: " + e.getMessage());
        }
    }

    /**
     * 上传文件
     * @param file 文件对象
     * @param type 文件类型（用于确定存储路径）
     * @return 文件访问URL
     */
    public String uploadFile(MultipartFile file, String type) {
        // 文件验证
        validateFile(file);
        validateContentType(file.getOriginalFilename(), file.getContentType());

        try {
            String fileName = generateFileName(file.getOriginalFilename());
            String objectName = generateObjectName(type, fileName);

            // 执行文件上传
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectName)
                            .stream(file.getInputStream(), file.getSize(), -1)
                            .contentType(file.getContentType())
                            .build()
            );

            // 返回文件访问URL
            return generateFileUrl(objectName);

        } catch (Exception e) {
            log.error("文件上传失败: {}", e.getMessage(), e);
            if (e instanceof IOException) {
                throw new BusinessException("文件读取失败，请重试");
            } else if (e instanceof ErrorResponseException) {
                throw new BusinessException("存储服务异常，请联系管理员");
            }
            throw new BusinessException("文件上传失败: " + e.getMessage());
        }
    }

    /**
     * 删除文件
     * @param objectName 文件对象名称
     */
    public void deleteFile(String objectName) {
        try {
            minioClient.removeObject(
                    RemoveObjectArgs.builder()
                            .bucket(minioConfig.getBucketName())
                            .object(objectName)
                            .build()
            );
        } catch (Exception e) {
            log.error("文件删除失败: {}", e.getMessage(), e);
            throw new BusinessException("文件删除失败: " + e.getMessage());
        }
    }

    /**
     * 获取文件访问URL
     * @param objectName 文件对象名称
     * @return 文件访问URL
     */
    public String getFileUrl(String objectName) {
        return generateFileUrl(objectName);
    }

    // 私有辅助方法

    /**
     * 文件基础验证
     */
    private void validateFile(MultipartFile file) {
        if (file == null || file.isEmpty()) {
            throw new BusinessException("文件不能为空");
        }

        if (file.getSize() > MAX_FILE_SIZE) {
            throw new BusinessException("文件大小不能超过10MB");
        }

        String contentType = file.getContentType();
        boolean isValidType = false;
        for (String allowedType : ALLOWED_CONTENT_TYPES) {
            if (allowedType.equals(contentType)) {
                isValidType = true;
                break;
            }
        }
        if (!isValidType) {
            throw new BusinessException("不支持的文件类型，只支持jpg/png/gif/webp格式");
        }
    }

    /**
     * 验证文件内容类型
     */
    private void validateContentType(String filename, String contentType) {
        String extension = getFileExtension(filename);
        String expectedContentType = EXTENSION_TO_CONTENT_TYPE.get(extension.toLowerCase());
        if (expectedContentType == null || !expectedContentType.equals(contentType)) {
            throw new BusinessException("文件类型不匹配");
        }
    }

    /**
     * 获取文件扩展名
     */
    private String getFileExtension(String fileName) {
        if (fileName == null || !fileName.contains(".")) {
            throw new BusinessException("无效的文件名");
        }
        return fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
    }

    /**
     * 生成文件名
     */
    private String generateFileName(String originalFilename) {
        return UUID.randomUUID().toString().replace("-", "") +
                getFileExtension(originalFilename);
    }

    /**
     * 生成对象名称
     */
    private String generateObjectName(String type, String fileName) {
        return type + "/" + fileName;
    }

    /**
     * 生成文件访问URL
     */
    private String generateFileUrl(String objectName) {
        try {
            return String.format("%s/%s/%s",
                    minioConfig.getEndpoint(),
                    minioConfig.getBucketName(),
                    objectName);
        } catch (Exception e) {
            log.error("生成文件URL失败", e);
            throw new BusinessException("生成文件访问地址失败");
        }
    }
}